﻿using System;
using System.Drawing;
using System.Windows.Forms;

namespace SnakeGame {
    // 蛇
    class Snake {
        public SnakeBody Head { get; private set; } // 蛇头
        private SnakeBody mCauda;                   // 蛇尾
        private Keys mDirection;                    // 移动方向

        /**
         * 构造方法。
         * @param drawer    绘图者
         */
        public Snake(Graphics drawer) {
            // 初始化成员
            Point caudaPos = new Point(0, 0);
            Head = mCauda = new SnakeBody(drawer, caudaPos, SnakeBody.Type.SNAKE);
            mDirection = Keys.Down;
        }

        /**
         * 吃果子，即将果子放到蛇尾。
         * @param apple 果子
         */
        public void Eat(SnakeBody apple) {
            // 设置吃到的果子的位置
            Point position = Head.Position;
            switch (mDirection) {
                case Keys.Down:
                    position.Y++;
                    break;
                case Keys.Left:
                    position.X--;
                    break;
                case Keys.Right:
                    position.X++;
                    break;
                case Keys.Up:
                    position.Y--;
                    break;
            }
            apple.Position = position;

            // 改变果子的类型
            apple.SetType(SnakeBody.Type.SNAKE);

            // 重绘蛇头和果子
            Head.Draw();
            apple.Draw();

            // 将吃到的果子变成蛇头
            Head.Next = apple;
            Head = apple;
        }

        /**
         * 移动。
         * @return 蛇头碰到自身或游戏区域边界则返回 true，否则返回 false
         */
        public bool Move() {
            // 计算蛇尾的新位置并抹除蛇尾
            Point point = new Point(Head.Position.X, Head.Position.Y);
            switch (mDirection) {
                case Keys.Down:
                    if (point.Y + 1 >= Const.GAME_BOARD_GRID_COUNT_V) {
                        // 如果蛇头超出游戏区域下边界，则返回
                        return true;
                    }
                    mCauda.Erase();
                    point.Y++;
                    break;
                case Keys.Left:
                    if (point.X - 1 < 0) {
                        // 如果蛇头超出游戏区域左边界，则返回
                        return true;
                    }
                    mCauda.Erase();
                    point.X--;
                    break;
                case Keys.Right:
                    if (point.X + 1 >= Const.GAME_BOARD_GRID_COUNT_H) {
                        // 如果蛇头超出游戏区域右边界，则返回
                        return true;
                    }
                    mCauda.Erase();
                    point.X++;
                    break;
                case Keys.Up:
                    if (point.Y - 1 < 0) {
                        // 如果蛇头超出游戏区域上边界，则返回
                        return true;
                    }
                    mCauda.Erase();
                    point.Y--;
                    break;
            }
            mCauda.Position = point;

            // 将蛇尾的下一个结点设置为蛇尾、原蛇尾设置成蛇头
            Head.Next = mCauda;
            Head = mCauda;
            mCauda = mCauda.Next;
            Head.Next = null;
            Head.Draw();

            // 检查蛇头是否碰到蛇身
            for (SnakeBody snakeBody = mCauda; snakeBody != Head; snakeBody = snakeBody.Next) {
                if (snakeBody.Position.Equals(Head.Position)) {
                    return true;
                }
            }

            return false;
        }

        /**
         * 检查某点是否与蛇身位置重叠。
         * @param point 被检查的点
         * @return 点与蛇身位置重叠则返回 true ，否则返回 false
         */
        public bool PointIsOnMe(Point point) {
            for (SnakeBody snakeBody = mCauda; snakeBody != null; snakeBody = snakeBody.Next) {
                if (snakeBody.Position.Equals(point)) {
                    return true;
                }
            }
            return false;
        }

        /**
         * 转弯。
         * @param direction 方向
         */
        public void Turn(Keys direction) {
            // 如果方向与当前方向相反（想要直接调头），则直接返回
            if (Math.Abs(direction - mDirection) == 2) {
                return;
            }
            mDirection = direction;
        }
    }
}
